﻿using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
using SimpleSpreadsheet.Framework;
using Action = System.Action;
using Mouse = System.Windows.Input.Mouse;

namespace SimpleSpreadsheet.Controls
{
    /// <summary>
    /// Base control for Workbook and Worksheet views
    /// </summary>
    public class WorkBaseControl : UserControl
    {
        protected DispatcherTimer _timer1;
        protected DispatcherTimer _timer3;
        protected BackgroundWorker _worker;
        protected ProgressDialog pd;
        protected event Action<RunWorkerCompletedEventArgs> BackgroundOperationCompleted;

        protected void InvokeBackgroundOperationCompleted(RunWorkerCompletedEventArgs e)
        {
            Action<RunWorkerCompletedEventArgs> handler = BackgroundOperationCompleted;
            if (handler != null)
                handler(e);
        }

        /// <summary>
        /// Locates the ViewModel object for the current view
        /// </summary>
        /// <returns></returns>
        protected object GetViewModel()
        {
            return ViewModelLocator.LocateForView(this);
        }

        /// <summary>
        /// Invokes potentially long-running operation in background
        /// </summary>
        /// <param name="operation"></param>
        /// <param name="viewModelArgs"></param>
        protected void InvokeOperation(ViewModelOperation operation, params object[] viewModelArgs)
        {
            pd = null;

            _timer1 = new DispatcherTimer();
            _timer1.Interval = new TimeSpan(0, 0, 0, 0, 100);
            _timer1.Tick += _timer1_Tick;
            _timer1.Start();

            _timer3 = new DispatcherTimer();
            _timer3.Interval = new TimeSpan(0, 0, 3);
            _timer3.Tick += _timer3_Tick;
            _timer3.Start();

            _worker = new BackgroundWorker();
            _worker.WorkerSupportsCancellation = true;
            _worker.DoWork += delegate
                                  {
                                      object viewModel = Dispatcher.Invoke(new Func<object>(GetViewModel));
                                      operation(viewModel, viewModelArgs);
                                      //System.Threading.Thread.Sleep(5000);
                                  };
            _worker.RunWorkerCompleted += _worker_RunWorkerCompleted;
            _worker.RunWorkerAsync();
        }

        /// <summary>
        /// Changes the cursor to 'Wait' when operation took already 100ms
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void _timer1_Tick(object sender, EventArgs e)
        {
            Dispatcher.BeginInvoke(new Action(UpdateCursor));

            var dt = sender as DispatcherTimer;
            if (dt != null)
                dt.Stop();
        }

        /// <summary>
        /// Displays the progress indicator dialog when operation took already 3 seconds
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void _timer3_Tick(object sender, EventArgs e)
        {
            pd = new ProgressDialog();
            pd.Cancel += CancelProcess;
            pd.ShowDialog();

            var dt = sender as DispatcherTimer;
            if (dt != null)
                dt.Stop();
        }

        /// <summary>
        /// Changes cursor to 'Wait'
        /// </summary>
        protected void UpdateCursor()
        {
            Mouse.OverrideCursor = Cursors.Wait;
        }

        /// <summary>
        /// Handler for worker completed event
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void _worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            _timer1.Stop();
            _timer3.Stop();

            if (pd != null)
                pd.Close();

            Mouse.OverrideCursor = null;
            InvokeBackgroundOperationCompleted(e);
        }

        /// <summary>
        /// Cancels the operation
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void CancelProcess(object sender, EventArgs e)
        {
            _timer1.Stop();
            _timer3.Stop();
            if (pd != null)
                pd.Close();
            Mouse.OverrideCursor = null;
            _worker.CancelAsync();
        }

        /// <summary>
        /// Finds first visual child of specified type under specified control
        /// </summary>
        /// <typeparam name="T">type to find</typeparam>
        /// <param name="depObj">parent control</param>
        /// <returns></returns>
        protected T FindVisualChild<T>(DependencyObject depObj) where T : DependencyObject
        {
            if (depObj != null)
            {
                for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
                {
                    DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
                    if (child != null && child is T)
                    {
                        return (T) child;
                    }

                    var childItem = FindVisualChild<T>(child);
                    if (childItem != null) return childItem;
                }
            }
            return null;
        }

        #region Nested type: ViewModelOperation

        protected delegate void ViewModelOperation(object viewModel, params object[] args);

        #endregion
    }
}